#load reliance libarary
import pygame
from pygame.locals import *
import random
import time
import threading
import queue
import pickle
import socket

#from classes import CAvatar 
from classes import CBackground 
from classes import CBullet 
from classes import CPlane 
from classes import CEnemy 
from classes import CTimer
from classes import CExplosion
from classes import COnlineplane
from classes import CConnector
from classes import CSBullet
from classes import Blood
class CBattleground:
    
    def __init__(self):
        self.running = True
        self.keepGoing = True
        self.pause = False
        self.ready= False
        self.history = False
        self.width=480
        self.height=640
        self.online = False
        
        self.pygame = pygame
        self.screen = self.pygame.display.set_mode((self.width, self.height))
        self.pygame.font.init()
        self.pygame.init()
        self.pygame.mixer.init()
        self.gameover_sound = pygame.mixer.Sound("resources/audio/moonlight.wav")
        self.explosions_sound = pygame.mixer.Sound("resources/audio/explode.wav")
        self.bloodget_sound = pygame.mixer.Sound("resources/audio/blood.wav")
        #bullets list
        self.bullets =[]


        #Blood list
        self.bloods=[]


        #enmeyBullets list
        self.enemyBullets =[]

        self.plane = CPlane.CPlane()

        #enemyplanes
        self.enemyPlanes = []
       

        #explosions
        self.explosions = []

        #battle time lasting
        self.time =time.time()
        self.progress = 0

        self.clockQueue = queue.Queue()
        self.enemyBulletsQ = queue.Queue()
        self.heartBeatQueue = queue.Queue()
        self.pauseQ = queue.Queue()
        self.bloodq = queue.Queue()
        self.showtxts = queue.Queue()
        self.messageQ = queue.Queue()
        self.controlQ = queue.Queue()

        self.gameover = False
        self.score = 0

        self.ipAddress = "127.0.0.1"

        return 

    def init(self, historyData =[]):
        if not historyData:
            self.running = True
            self.keepGoing = True
            self.width=480
            self.height=640

            #bullets list
            self.bullets =[]

            #Blood list
            self.bloods=[]


            #enmeyBullets list
            self.enemyBullets =[]

            #enemyplanes
            self.enemyPlanes = []


            #explosions
            self.explosions = []

            #battle time lasting
            self.time =time.time()
            self.progress = 0
            self.gameover = False
            self.score = 0

            self.plane = CPlane.CPlane()
        else:
            for key in historyData:
                if key == "enemyBullets":
                    for tmp in historyData[key]:
                        b = CBullet.CBullet(tmp["position"])
                        self.enemyBullets.append(b)
                elif key=="enmyPlanes" :
                    for tmp in historyData[key]:
                        b = CEnemy.CEmemy(tmp["position"])
                        self.enemyPlanes.append(b)
                elif key=="explosions" :
                    for tmp in historyData[key]:
                        b = CExplosion.CExplosion(tmp["position"])
                        self.explosions.append(b)
                elif key =="bullets":
                    for tmp in historyData[key]:
                        b = CBullet.CBullet(tmp["position"])
                        self.bullets.append(b)
                elif key =="plane":
                    self.plane = CPlane.CPlane(historyData[key]["position"], historyData[key]["healthValue"])
                else:
                    setattr(self,key,historyData[key]) 


        self.pygame = pygame
        self.pygame.font.init()
        self.pygame.init()
        self.clockQueue = queue.Queue()
        self.enemyBulletsQ = queue.Queue()
        self.heartBeatQueue = queue.Queue()
        self.bloodq = queue.Queue()
        
        return 
    
    def drawScore(self):
        pygame.font.init()
        font = self.pygame.font.Font(None,24)
        scoreText = font.render(str("score:")+str(self.score),True,(255,255,0))
        textRect = scoreText.get_rect()
        textRect.topright = [self.width-5,5]
        self.screen.blit(scoreText, textRect)

    def bulletsControl(self):
        #create player's bullets
        for bullet in self.bullets:
            bullet.move()
            if bullet.position[0]<0 or bullet.position[0]>self.width or bullet.position[1]<0 or bullet.position[1]>self.height:
                self.bullets.remove(bullet)

        for bullet in self.bullets:
            self.screen.blit(bullet.image,(bullet.position[0],bullet.position[1]))
            
 
        #create enemie's bullets
        if not self.enemyBulletsQ.empty():
            i = self.enemyBulletsQ.get()
            for enemyPlane in self.enemyPlanes:
                i = random.randint(0,2)
                if i == 0:
                    b = CBullet.CBullet([enemyPlane.position[0], enemyPlane.position[1]])
                    self.enemyBullets.append(b) 

        for bullet in self.enemyBullets:
            bullet.moveDown()
            if bullet.position[0]<0 or bullet.position[0]>self.width or bullet.position[1]<0 or bullet.position[1]>self.height:
                self.enemyBullets.remove(bullet)
            self.screen.blit(bullet.image,(bullet.position[0],bullet.position[1]))
        return


       

    def sendMessage():
        return

    def battleClock(self):
        timer = CTimer.CTimer(1,self.clockQueue,self.pauseQ)
        timer.start()

        bloodsupply = CTimer.CTimer(5,self.bloodq,self.pauseQ)
        bloodsupply.start()

        enenmyBulletT = CTimer.CTimer(0.5,self.enemyBulletsQ,self.pauseQ)
        enenmyBulletT.start()

        heartBeatT =CTimer.CTimer(0.025, self.heartBeatQueue, self.pauseQ)
        heartBeatT.start()

    def getTime():
        return  int(time.time() - self.time)

    #ememyPlanesControl
    def enemyPlanesControl(self):
        #put 4 enemies in the battleground every 1 second        
        if not self.clockQueue.empty():
            count = random.randint(0,4)
            for i in range(count):
                x = random.randint(0,self.width-60)
                e = CEnemy.CEnemy([x,0]) 
                self.enemyPlanes.append(e)
            i = self.clockQueue.get()
    
        #check enemy planes
        for enemyPlane in self.enemyPlanes:
            enemyPlane.move()
            if enemyPlane.position[0]<0 or enemyPlane.position[0]>self.width or enemyPlane.position[1]<0 or enemyPlane.position[1]>self.height:
                self.enemyPlanes.remove(enemyPlane)
            
            else:
                #painting
                self.screen.blit(enemyPlane.image,(enemyPlane.position[0],enemyPlane.position[1]))
             
    #Blood
    def BloodControl(self):
        #put 1 BloodSupply in the battleground every 10 second        
        if not self.bloodq.empty():
            count = random.randint(0,1)
            for i in range(count):
                x = random.randint(0,self.width-60)
                y = random.randint(0,self.height-400)
                e = Blood.Blood([x,y])
        
                self.bloods.append(e)
            i=self.bloodq.get()    
         #check blood
        for Bloode in self.bloods:
            Bloode.move()
            if Bloode.position[0]<0 or Bloode.position[0]>self.width or Bloode.position[1]<0 or Bloode.position[1]>self.height:
                self.bloods.remove(Bloode)
                
                
            else:
                #painting
                self.screen.blit(Bloode.image,(Bloode.position[0],Bloode.position[1]))
                

    def explosionsControl(self):
        if not self.heartBeatQueue.empty():
            e = self.heartBeatQueue.get()
            for explosion in self.explosions:
                explosion.lifeValue -=1
                if explosion.lifeValue == 0:
                    self.explosions.remove(explosion)
                    self.explosions_sound.play()
        #painting
        for explosion in self.explosions:
            self.screen.blit(explosion.image,explosion.position)

    #------------------battle control--------------------
    def battleControl(self):
        #collision detect
        planeRect = self.pygame.Rect(self.plane.image.get_rect())
        planeRect.left = self.plane.position[0]
        planeRect.top = self.plane.position[1]

        for enemy in self.enemyPlanes:
            enemyRect = self.pygame.Rect(enemy.image.get_rect())
            enemyRect.left = enemy.position[0]
            enemyRect.top = enemy.position[1]
            
            if(planeRect.colliderect(enemyRect)):
                e = CExplosion.CExplosion([enemy.position[0],enemy.position[1]])
                self.explosions.append(e)
                e = CExplosion.CExplosion([self.plane.position[0],self.plane.position[1]])
                self.explosions.append(e)

                self.plane.healthValue =0
                self.gameover = True
                self.running = False

            for bullet in  self.bullets:
                bulletRect = self.pygame.Rect(bullet.image.get_rect())
                bulletRect.left = bullet.position[0]
                bulletRect.top= bullet.position[1]
                if enemyRect.colliderect(bulletRect):
                    #hit by bullet 
                    self.bullets.remove(bullet)
                    self.enemyPlanes.remove(enemy)
                    #add explosion
                    e = CExplosion.CExplosion([enemy.position[0],enemy.position[1]])
                    self.explosions.append(e)
                    self.score +=1

        for blood in self.bloods:
            bloodRect = self.pygame.Rect(blood.image.get_rect())
            bloodRect.left = blood.position[0]
            bloodRect.top = blood.position[1]

            if(bloodRect.colliderect(planeRect)):
                self.bloods.remove(blood)
                self.plane.healthValue+=20
                #self.createText(24,"Tips:", blood.position[0]+20,blood.position[1])
                if(self.plane.healthValue >=194):
                    self.plane.healthValue=194
                self.bloodget_sound.play()

        for bullet in self.enemyBullets:
            bulletRect = self.pygame.Rect(bullet.image.get_rect())
            bulletRect.left = bullet.position[0]
            bulletRect.top= bullet.position[1]
            if planeRect.colliderect(bulletRect):
                self.plane.healthValue -=20
                self.enemyBullets.remove(bullet)
                if(self.plane.healthValue<=0):    
                    self.gameover = True
                    self.running = False


        #paint the health value
        self.screen.blit(self.plane.image,self.plane.position)
        if self.online:
            self.screen.blit(self.onlineplane.image,self.onlineplane.position)

        self.screen.blit(self.plane.healthbar,(5,5))
        for health1 in range(self.plane.healthValue):
            self.screen.blit(self.plane.health,(health1+8,8))
        #paint score
        self.drawScore()


    def createText(self, fontSize, text, x,y):
        self.pygame.font.init()
        font = self.pygame.font.Font(None, fontSize)
        scoreText = font.render(str(text),True,(255,255,0))
        textRect = scoreText.get_rect()
        textRect.top = x 
        textRect.left = y
        
        self.screen.blit(scoreText, textRect)

    def __getKey(self):
        while True:
            event = self.pygame.event.poll()
            if event.type == KEYDOWN:
                return event.key
            else:
                pass
    def __displayInput(self, message, pos =[100,100], showMsg ="", pos2=[80,80], fontSize =36):
        self.screen = self.pygame.display.set_mode((self.width, self.height))
        self.createText(fontSize, showMsg,pos2[0],pos2[1])
        self.createText(fontSize, message,pos[0],pos[1])
        self.createText(24,"local ip:"+ self.getIp(), 300,10)
        self.pygame.display.flip()

    def __askInput(self,question, pos =[80,80], fontSize="36"):
        pygame.font.init()
        currentString = [] 
        if  question == "Please Eneter Ip Address:":
            ip = self.getIp()
            p = ip.rfind(".")
            ip = ip[0:p+1]
            currentString = list(ip) 
        while True:
            self.__displayInput("".join(currentString), [100,100], question, pos, fontSize)
            inkey = self.__getKey()
            if inkey == K_BACKSPACE:
                currentString = currentString[0:-1]
            elif inkey == K_RETURN:
                break
            elif inkey == K_MINUS:
                cureentString.append("_")
            elif inkey <= 127:
                currentString.append(chr(inkey))

        return  "".join(currentString)


    #---------prepare----------
    def prepare(self):
        #get keyboard in
        if self.history == False:
            choice= self.__askInput("Please  press L or O to choose local or online mode:", [80,0], 24)
            if(choice == "o" or choice == "O"):
                choice2 = self.__askInput("Please  press S or C to choose act as service or client:", [80, 0], 24)
                if (choice2 == "c" or choice2 == "C"):
                    self.isService = False
                    self.ipAddress = self.__askInput("Please Eneter Ip Address:", [80, 80], 36)
                elif choice2 == "s" or choice2 == "S":
                    self.isService = True
                self.online = True

                if self.isService == True:
                    self.plane = CPlane.CPlane([0, 600])
                    self.onlineplane = COnlineplane.COnlineplane([400,600], 194, False)
                else:
                    self.plane = CPlane.CPlane([400, 600], 194, False)
                    self.onlineplane = COnlineplane.COnlineplane([0,600])

            elif choice == "l" or choice =="L":
                pass
        #self.ipAddress = "192.168.105.243"


        # establish a connection
        if self.online == True :
            #clinent accept  or server send
            connector = CConnector.CConnector(self.isService, self.messageQ, self.controlQ,123, True,self.ipAddress)
            connector.start()
            
            #client send  or server accept 
            connectorRecv = CConnector.CConnector(self.isService, self.messageQ, self.controlQ, 321, False, self.ipAddress)
            connectorRecv.start()

            while True:
                if self.controlQ.empty():
                    pass
                else:
                    e = self.controlQ.get()
                    break
    
        self.history = False

        self.screen = self.pygame.display.set_mode((self.width, self.height))
        self.createText(24,"Tips:", self.height*0.1,0)
        self.createText(24,"Use the 'A''W''S''D'to move,Use the mouse to shoot!", self.height*0.2,0)
        self.createText(24,"use the F8 to store current record! and H to restore", self.height*0.3,0)
        self.createText(24,"use the Space to pause anytime!", self.height*0.4,0)
        self.createText(24,"Press Enter to Start!Good luck!", self.height*0.5,0)
        self.createText(36,"Ready?GO!", self.height*0.6,0)

        while not self.ready:
            if self.online == False:
                for event in self.pygame.event.get():
                    if event.type == self.pygame.KEYDOWN:
                        if event.key == self.pygame.K_RETURN:
                            self.ready = True
            else:
                if self.isService == True:
                    for event in self.pygame.event.get():
                        if event.type == self.pygame.KEYDOWN:
                            if event.key == self.pygame.K_RETURN:
                                self.ready = True
                else:
                    if not self.controlQ.empty():
                        self.ready = True
                    
            self.pygame.display.flip()

    #-------------------Main--------------------------
    def run(self):
        #load elements
        self.screen = self.pygame.display.set_mode((self.width, self.height))
        background= CBackground.CBackground(self.height)

        keys = [False, False, False, False]   #move parameter

        #calcualte battle time
        self.battleClock()

        while(self.running):
       
            if not self.pause: 
                background.move(self.height)
                self.screen.blit(background.image,background.position)
                
                #move plane
                self.plane.move(keys,self.height,self.width, self.online)
                if self.online == True:
                    data = self.plane.position[:]
                    data.append("0")
                    self.messageQ.put(data)

                #move online plane
                #if self.online == True:
                if not self.controlQ.empty():
                    onlinedata = self.controlQ.get()
                    self.onlineplane.move(self.height, self.width, onlinedata)

                    if onlinedata :
                        data = onlinedata.split(";")
                        data = data[0].split(",")
                        if len(data)>=3:
                            shot = float(data[2])
                            #send bullet
                            if(shot == True):
                                b = CBullet.CBullet([self.onlineplane.position[0]+32,self.onlineplane.position[1]+32]) 
                                self.bullets.append(b)
                            
                        


                #control bullets             
                self.bulletsControl()

                #control enmeyPlanes             
                self.enemyPlanesControl()

                #check elemetns in battleground
                self.battleControl() 

                #explosion control
                self.explosionsControl()

                #Blood Control
                self.BloodControl()

                #paint
                self.pygame.display.flip()

            #listen event 
            for event in self.pygame.event.get():
                if event.type == self.pygame.KEYDOWN:
                    if event.key==K_w:
                        keys[0]=True
                    elif event.key==K_a:
                        keys[1]=True
                    elif event.key==K_s:
                        keys[2]=True
                    elif event.key==K_d:
                        keys[3]=True

                    if  event.key == K_SPACE:
                        self.pause = not self.pause
                        if self.pause == True:
                            self.pauseQ.put(1) 
                        else:
                            if not self.pauseQ.empty():
                                self.pauseQ.queue.clear()
                    if event.key == K_F8:
                        self.save()
                        self.pause = not self.pause
                        if self.pause == True:
                            self.pauseQ.put(1) 
                        else:
                            if not self.pauseQ.empty():
                                self.pauseQ.queue.clear()
                    if event.key == self.pygame.K_h:
                        #load history
                        self.history =True
                        return
                    if event.key == self.pygame.K_RETURN:
                        b = CBullet.CBullet([self.plane.position[0]+32,self.plane.position[1]+32]) 
                        self.bullets.append(b)

                        data = []
                        data.append(self.plane.position[0])
                        data.append(self.plane.position[1])
                        data.append("1") 
                        self.messageQ.put(data)
                        print("message queue")
                        print(data)

                if event.type == self.pygame.KEYUP:
                    if event.key==self.pygame.K_w:
                        keys[0]=False
                    elif event.key==self.pygame.K_a:
                        keys[1]=False
                    elif event.key==self.pygame.K_s:
                        keys[2]=False
                    elif event.key==self.pygame.K_d:
                        keys[3]=False

                

                if event.type==self.pygame.MOUSEBUTTONUP:
                    #generate bullet 
                    b = CSBullet.CSBullet([self.plane.position[0]+40,self.plane.position[1]+32])
                    c=CBullet.CBullet([self.plane.position[0]+20,self.plane.position[1]+32])
                    self.bullets.append(b)
                    data = []
                    data.append(self.plane.position[0])
                    data.append(self.plane.position[1])
                    data.append("1")
                    self.messageQ.put(data)
                    self.bullets.append(c)

        if(self.gameover):
            self.pygame.font.init()
            font = self.pygame.font.Font(None,24)
            gameoverImage =self.image=self.pygame.image.load("resources/images/gameover.png")
            self.screen.blit(gameoverImage,(-100,100))
            self.gameover_sound.play(0,11000)
        else:
            youwin=self.image=self.pygame.image.load("resources/images/youwin.png")
            self.screen.blit(youwin,(-100,100)) 

        self.ending()

    #tackle with ending
    def ending(self):
        '''self.pygame.font.init()
        font = self.pygame.font.Font(None,36)
        scoreText = font.render(str("Press R to Restart! or 'Q' to quit!"),True,(255,255,0))
        textRect = scoreText.get_rect()
        textRect.top = self.height*2/3
        self.screen.blit(scoreText, textRect)
           ''' 
        self.createText(36, "Press R to Restart! or 'Q' to quit!",self.height*0.25, 0)
        self.createText(36, " or 'H' to History",self.height*0.3, 0)

        while True:
            self.keepGoing = False
            for event in self.pygame.event.get():
                if event.type == self.pygame.QUIT:
                    self.pygame.quit()
                    return
                elif event.type == self.pygame.KEYDOWN:
                    if event.key == self.pygame.K_r:
                        self.keepGoing = True
                        pygame.mixer.stop()
                        return
                    elif event.key == self.pygame.K_q:
                        self.pygame.quit()
                        return
                    elif event.key == self.pygame.K_h:
                        self.keepGoing = True
                        self.history =True
                        pygame.mixer.stop()
                        return
                    else:
                        continue
            self.pygame.display.flip()

       
    def save(self):
        historyData = {}
        #data to save
        historyData["running"] = True
        historyData["keepGoing"] = True
        historyData["width"]=480
        historyData["height"]=640
        #bullets list
        historyData["bullets"] =[]
        #enmeyBullets list
        historyData["enemyBullets"] =[]
        #enemyplanes
        historyData["enemyPlanes"] = []
        #explosions
        historyData["explosions"] = []

        #player
        historyData["plane"] =""

        #battle time lasting
        historyData["time"] =time.time()
        historyData["progress"] = 0
        historyData["gameover"] = False
        historyData["score"] = 0

        #get current data
        for key in historyData:
            if(key == "enemyBullets" or key=="enmyPlanes" or key=="explosions" or key =="bullets"):
                tmpData = getattr(self,key)
                for tmp in  tmpData:
                    historyData[key].append({"position":tmp.position}) 
            elif key == "plane":
                plane = getattr(self,key)
                historyData[key] = {"position": plane.position, "healthValue":plane.healthValue}  
            else:
                historyData[key] = getattr(self,key)
            
        #open file
        filename = "./record/record.log"
        fh = open(filename,"wb")
        pickle.dump(historyData,fh)
        fh.close() 
        return
    def getHistory(self):
        filename = "./record/record.log"
        historyData = {}
        try:
            fh = open(filename,"rb")
            historyData = pickle.load(fh)
        except  Exception as e:
            print("no history data")
         
        return historyData
                
    def getIp(self):
        ip =""
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(('8.8.8.8', 80))
            ip = s.getsockname()[0]
        finally:
            return ip
